输出格式化字符串：
    %p   打印指针地址
    %d   打印整数
    %f   打印浮点数
    %s   打印字符串
    %c   打印字符
    %x   打印十六进制整数
    %o   打印八进制整数
    %e   打印科学计数法
    %g   打印自动选择的浮点数或科学计数法
    %u   打印无符号整数

区别：
    printf("格式控制",输出列表)
    (1)将内存中的数据转成指定格式的字符串
    （2）将字符串输出显示器
    sprintf(地址，"格式控制",输出列表)
    (1)将内存中的数据转成指定格式的字符串
    （2）将字符串保存到地址指向空间
    fprintf(文件描述符,"格式控制",输出列表)
    (1)将内存中的数据转成指定格式的字符串
    （2）将字符串保存到文本文件中
    snprintf(地址，最大长度，"格式控制",输出列表)
    (1)将内存中的数据转成指定格式的字符串
    （2）将字符串保存到地址指向空间，但不超过最大长度

void*函数原型：
    void* malloc(size_t size);
    void free(void* ptr);
    void* memcpy(void* dest, const void* src, size_t n);
    void* memmove(void* dest, const void* src, size_t n);
    void* memchr(const void* s, int c, size_t n);
    int memcmp(const void* s1, const void* s2, size_t n);
    void* memset(void* s, int c, size_t n);

四大指针 ：void*  二级指针 函数指针 数组指针

二级指针：
    二级指针是指指向指针的指针，即指向一个指针变量，该指针变量指向另一个变量。
    二级指针的声明：
        类型名 **变量名;
    二级指针的使用：
        变量名 = (类型名 **)malloc(sizeof(类型名 *));
        *变量名 = (类型名 *)malloc(sizeof(类型名));
        (*变量名) = 值;
        值 = *变量名;
        free(*变量名);
        free(变量名);   //释放二级指针变量
        free(指针变量名); //释放一级指针变量



 void *指针
     1、文件读写函数 内存管理函数  消息队列读写函数 网络收发函数等使用void*类型修饰函数参数。
     2、含有void *类型参数的函数，处理数据按照字节处理，不区分数据类型
     3、含有void *类型参数的函数，配合一个表示字节多少的参数。
     4、含有void *类型参数函数实现原理：
                (1) 将void *强制转换成 char *
                (2) 按照字节处理

void *函数类型
    函数指针：
    一、概念
        1、函数指针：指向函数的指针
            函数在内存中起始地址，用函数名表示
        2、定义函数指针变量
            返回值类型 (*变量名)(参数类型1，参数类型2，...) = NULL;
        3、指向
            变量名 = 函数名;
        4、调用
            (*指针变量名)(实参1，实参2，...)

    二、编程
        1、定义函数
            int add(int a,int b)
            {}
            int sub(int a,int b) = NULL;
            或者

            typedef int (*fun)(int,int);
            fun sub = NULL;

        2、特点
            声明时，返回值类型前加上(*)，表示函数指针
            定义时，函数名前加上(*)，表示函数指针
            调用时，(*指针变量名)(实参1，实参2，...)
            注意：指针变量名必须是函数指针类型，不能是普通指针类型
        3、注意事项
            函数指针变量的定义和使用必须在同一文件中，不能跨文件
            函数指针变量的生命周期和函数相同
            函数指针变量的赋值必须是函数名，不能是函数指针变量
            函数指针变量的默认值是NULL，表示没有指向任何函数
            函数指针变量的赋值和调用都需要加上括号，即(*指针变量名)(实参1，实参2，...)
数组指针：
    数组指针是指指向数组的指针，即指向一个数组变量的指针。
    数组指针的声明：
        类型名 (*变量名)[元素个数];
    数组指针的使用：
        变量名 = (类型名 (*)[元素个数])malloc(sizeof(类型名) * 元素个数);
        变量名[下标] = 值;
        值 = 变量名[下标];
        free(变量名); //释放数组指针变量

函数指针数组：
    数组中存放函数指针，可以根据数组下标调用不同的函数。
    声明：
        函数指针数组名[数组元素个数];
    调用：
        函数指针数组名[下标](参数1，参数2，...);
            


数据库：
链接错误几种可能原因和解决方案

方式1：
   原因：找不到库文件
        （1）未安装库文件
        （2）库文件已安装，位置不对，链接时找不到
   解决方式：
        （1）查询库文件是否已经正确安装在合适位置
        （2）用-L 指明库文件搜索路径
 
方式2：
   原因：找不到库文件
        （1）库文件版本号和当前程序不兼容     
   解决方式：
         安装合适版本库文件   

方式3：
    原因：.h文件中的函数声明和.c中函数实现不一致
    解决方式：
          查询.h和.c文件，确保是一致


memcpy和memmove区别
    memcpy()：复制内存块，从源地址到目的地址，直到源地址和目的地址之间的数据长度为n
    memmove()：复制内存块，从源地址到目的地址，直到源地址和目的地址之间的数据长度为n，如果源地址和目的地址重叠，则覆盖源地址的数据


TCP和UDP的主要区别
    1.连接性
        TCP是面向连接的协议，它在传输数据之前需要建立连接(三次握手)，并在数据传输完成后关闭连接(四次挥手)。
            这种连接是全双工的，即数据可以在两个方向上同时传输。

        UDP则是无连接的协议，它不需要建立或关闭连接，发送方可以直接发送数据报，接收方也可以随时接收数据报。

    2.可靠性
        TCP提供可靠的传输服务。它使用确认和重传机制来确保数据正确、完整、有序地到达目的地。TCP还通过流量控制和拥塞控制机制来防止网络过载。

        UDP则提供不可靠的传输服务。它不检查数据包的顺序、错误或重传。如果数据包在传输过程中丢失或损坏，UDP不会采取任何补救措施。
            因此，UDP通常用于对实时性要求较高、但对数据可靠性要求不高的应用(如视频流、音频流、实时游戏等)

    3.头部开销
        TCP的头部开销相对较大，包含更多的控制信息，如序列号、确认号、窗口大小等。

        UDP的头部开销较小，只包含必要的字段，如源端口、目的端口、长度和校验和。

    4.传输效率
        由于TCP需要建立连接、确认数据、处理重传等，所以其传输效率相对较低。但这也使得TCP在需要可靠传输的场景下表现更好。

        UDP则不需要这些额外的步骤，因为其传输效率更高。但这也意味着UDP在传输过程中可能会丢失数据。

    5.应用场景
        TCP通常用于需要可靠传输的场景，如文件存储、电子邮件、远程登录等。

        UDP则常用于对实时性要求较高、但对数据可靠性要求不高的场景，如视频流、音频流、DNS查询、VoIP(网络电话)等。

    6.流量控制和拥塞控制
        TCP具有流量控制和拥塞控制机制，可以根据网络状况动态调整发送速率，以防止网络拥塞和丢包。

        UDP则没有这些机制，它只负责将数据报从源端发送到目的端，则不关心网络状况和数据传输质量。

    7.一对一、一对多、多对一和多对多通信
        TCP通常用于一对一的通信，即一个TCP连接只能有一个发送方和一个接收方。

        UDP则支持一对多、多对一和多对多的通信模式，可以实现广播和组播功能。


通过fcntl设置文件描述符为非阻塞模式
    int setnonblocking(int fd) 
        {
            int old_option = fcntl(fd, F_GETFL);
            int new_option = old_option | O_NONBLOCK;
            fcntl(fd, F_SETFL, new_option);
            return old_option;
        } 

c语言中、\t \r \n\f 什么意思
    \t \r \n都是转义字符，空格就是单纯的空格，输入时可以输入空格
    \t 的意思是:水平制表符。将当前位置移到下一个tab位置。
    \r 的意思是:回车。将当前位置移到本行的开头。
    \n 的意思是:回车换行。将当前位置移到下一行的开头。
    \f的意思是：换页。将当前位置移到下一页的开头。


关键字:
    static 作用：修饰局部变量，全局变量，函数，表示该变量或函数只在本文件中有效，其他文件不可见。
    const 作用：修饰局部变量，全局变量，函数，表示该变量或函数的值不能被修改。
         注意：const 修饰符只能修饰变量，不能修饰函数。
    extern 作用：声明全局变量或函数，表示该变量或函数在其他文件中定义。
    enum 作用：定义枚举类型，可以为变量赋值，表示变量的值只能为枚举类型中定义的常量。

